
package com.shiku.commons;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class IdWorker {
    protected static final Logger LOG = LoggerFactory.getLogger(IdWorker.class);


    private static IdWorker idWorker = new IdWorker(0L, 0L);
    private long workerId;


    public static long getId() {

        return idWorker.nextId();

    }


    private long datacenterId;
    private long sequence = 0L;

    private long twepoch = 1288834974657L;

    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = 0xFFFFFFFFFFFFFFFFL ^ -1L << (int) this.workerIdBits;
    private long maxDatacenterId = 0xFFFFFFFFFFFFFFFFL ^ -1L << (int) this.datacenterIdBits;
    private long sequenceBits = 12L;

    private long workerIdShift = this.sequenceBits;
    private long datacenterIdShift = this.sequenceBits + this.workerIdBits;
    private long timestampLeftShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;
    private long sequenceMask = 0xFFFFFFFFFFFFFFFFL ^ -1L << (int) this.sequenceBits;

    private long lastTimestamp = -1L;


    public IdWorker(long workerId, long datacenterId) {

        if (workerId > this.maxWorkerId || workerId < 0L) {

            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", new Object[]{Long.valueOf(this.maxWorkerId)}));

        }

        if (datacenterId > this.maxDatacenterId || datacenterId < 0L) {

            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", new Object[]{Long.valueOf(this.maxDatacenterId)}));

        }

        this.workerId = workerId;

        this.datacenterId = datacenterId;

        LOG.info(String.format("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", new Object[]{

                Long.valueOf(this.timestampLeftShift), Long.valueOf(this.datacenterIdBits), Long.valueOf(this.workerIdBits), Long.valueOf(this.sequenceBits), Long.valueOf(workerId)}));

    }


    public synchronized long nextId() {

        long timestamp = timeGen();


        if (timestamp < this.lastTimestamp) {

            LOG.error(String.format("clock is moving backwards.  Rejecting requests until %d.", new Object[]{Long.valueOf(this.lastTimestamp)}));

            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", new Object[]{
                    Long.valueOf(this.lastTimestamp - timestamp)
            }));

        }

        if (this.lastTimestamp == timestamp) {

            this.sequence = this.sequence + 1L & this.sequenceMask;

            if (this.sequence == 0L) {

                timestamp = tilNextMillis(this.lastTimestamp);

            }

        } else {

            this.sequence = 0L;

        }


        this.lastTimestamp = timestamp;


        return timestamp - this.twepoch << (int) this.timestampLeftShift | this.datacenterId << (int) this.datacenterIdShift | this.workerId << (int) this.workerIdShift | this.sequence;

    }


    protected long tilNextMillis(long lastTimestamp) {

        long timestamp = timeGen();

        while (timestamp <= lastTimestamp) {

            timestamp = timeGen();

        }

        return timestamp;

    }


    protected long timeGen() {

        return System.currentTimeMillis();

    }

}


